﻿using System.Reflection;
using Yz.Core.Tools;

namespace Yz.Core.Extensions
{
    public static class LinqExtension
    {
        /// <summary>
        ///     根据实体中的字段去重复
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="source"></param>
        /// <param name="keySelector"></param>
        /// <returns></returns>
        public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
            Func<TSource, TKey> keySelector)
        {
            var seenKeys = new HashSet<TKey>();
            foreach (var element in source)
                if (seenKeys.Add(keySelector(element)))
                    yield return element;
        }

        /// <summary>
        ///     从List中随机获取一个实例，并中List中删除该实例
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <param name="delGet">获取实例后是否从List中删除该实例</param>
        /// <returns></returns>
        public static T GetRandomItem<T>(this IList<T> list, bool delGet = true) where T : class
        {
            if (list.Count == 0) return null;
            var rm = new Random();
            var i = rm.Next(list.Count); //随机数值能超list总数
            var model = list[i];
            if (delGet) list.RemoveAt(i);
            return model;
        }

        /// <summary>
        ///     根据跳过数量获取下一个实体，如果找不到返回第一个
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="models"></param>
        /// <param name="skipCount"></param>
        /// <returns></returns>
        public static T GetNextFromList<T>(this IList<T> models, ref int skipCount)
        {
            var mo = models.Skip(skipCount).FirstOrDefault();
            if (mo == null)
            {
                skipCount = 0;
                mo = models.Skip(skipCount).FirstOrDefault();
            }

            return mo;
        }

        /// <summary>
        ///     对象深拷贝
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T DeepClone<T>(this T obj) where T : class
        {
            if (obj == null) return default;
            object targetDeepCopyObj;
            var targetType = obj.GetType();
            //值类型
            if (targetType.IsValueType)
            {
                targetDeepCopyObj = obj;
            }
            //引用类型
            else
            {
                targetDeepCopyObj = Activator.CreateInstance(targetType); //创建引用对象
                var memberCollection = obj.GetType().GetMembers();

                foreach (var member in memberCollection)
                    //拷贝字段
                    if (member.MemberType == MemberTypes.Field)
                    {
                        var field = (FieldInfo)member;
                        var fieldValue = field.GetValue(obj);
                        if (fieldValue is ICloneable)
                            field.SetValue(targetDeepCopyObj, (fieldValue as ICloneable).Clone());
                        else
                            field.SetValue(targetDeepCopyObj, DeepClone(fieldValue));
                    } //拷贝属性
                    else if (member.MemberType == MemberTypes.Property)
                    {
                        var myProperty = (PropertyInfo)member;

                        var info = myProperty.GetSetMethod(false);
                        if (info != null)
                            try
                            {
                                var propertyValue = myProperty.GetValue(obj, null);
                                if (propertyValue is ICloneable)
                                    myProperty.SetValue(targetDeepCopyObj, (propertyValue as ICloneable).Clone(), null);
                                else
                                    myProperty.SetValue(targetDeepCopyObj, DeepClone(propertyValue), null);
                            }
                            catch (Exception ex)
                            {
                                YzNLog.Error(ex, "对象深拷贝");
                            }
                    }
            }

            return (T)targetDeepCopyObj;
        }
    }
}
